home *** CD-ROM | disk | FTP | other *** search
- /* Address Resolution Protocol (ARP) functions. Sits between IP and
- * Level 2, mapping IP to Level 2 addresses for all outgoing datagrams.
- */
- #include "machdep.h"
- #include "mbuf.h"
- #include "timer.h"
- #include "iface.h"
- #include "ether.h"
- #include "ax25.h"
- #include "arp.h"
- #include "cmdparse.h"
-
- extern int32 ip_addr; /* Our IP address */
-
- int ec_output();
- int pether(),gether();
-
- int setcall(),psax25();
-
- /* Table of ARP hardware types */
- struct arp_type arp_type[] = {
- 0,
- 0,
- 0,
- NULLCHAR,
- NULLFP,
- NULLFP,
-
- /* 10 megabit Ethernet */
- 6, /* Ethernet address length */
- 0x800, /* Ethernet type field for IP */
- 0x806, /* Ethernet type field for ARP */
- ether_bdcst, /* Ethernet broadcast address */
- pether,
- gether,
-
- /* 3 megabit Ethernet */
- 0,
- 0,
- 0,
- NULLCHAR,
- NULLFP,
- NULLFP,
-
- /* AX.25 */
- 7, /* AX.25 address length */
- 0xCC, /* AX.25 pid field for IP */
- 0xCD, /* AX.25 pid field for ARP */
- (char *)&ax25_bdcst, /* AX.25 broadcast address */
- psax25,
- setcall,
- };
- #define NTYPES 4
-
- /* Hash table headers */
- struct arp_tab *arp_tab[ARPSIZE];
-
- struct arp_stat arp_stat;
-
- /* Resolve an IP address to a hardware address; if not found,
- * initiate query and return NULLCHAR. If an address is returned, the
- * interface driver may send the packet; if NULLCHAR is returned,
- * res_arp() will have saved the packet on its pending queue,
- * so no further action (like freeing the packet) is necessary.
- */
- char *
- res_arp(interface,hardware,target,bp)
- struct interface *interface; /* Pointer to interface block */
- int16 hardware; /* Hardware type */
- int32 target; /* Target IP address */
- struct mbuf *bp; /* IP datagram to be queued if unresolved */
- {
- struct arp_tab *arp_lookup(),*arp_add();
- void arp_output();
- register struct arp_tab *arp;
-
- if((arp = arp_lookup(hardware,target)) != NULLARP && arp->state == ARP_VALID)
- return arp->hw_addr;
- /* Create an entry and put the datagram on the
- * queue pending an answer
- */
- arp = arp_add(target,hardware,NULLCHAR,0);
- enqueue(&arp->pending,bp);
- arp_output(interface,hardware,target);
- return NULLCHAR;
- }
- /* Handle incoming ARP packets. This is almost a direct implementation of
- * the algorithm on page 5 of RFC 826, except for:
- * 1. Outgoing datagrams to unresolved addresses are kept on a queue
- * pending a reply to our ARP request.
- * 2. The names of the fields in the ARP packet were made more mnemonic.
- */
- void
- arp_input(interface,bp)
- struct interface *interface;
- struct mbuf *bp;
- {
- struct arp arp;
- struct arp_tab *arp_lookup(),*ap;
- struct arp_type *at;
- struct mbuf *htonarp();
-
- arp_stat.recv++;
- if(ntoharp(&arp,bp) == -1) /* Convert into host format */
- return;
- if(arp.hardware >= NTYPES){
- /* Unknown hardware type, ignore */
- arp_stat.badtype++;
- return;
- }
- at = &arp_type[arp.hardware];
- if(arp.protocol != at->iptype){
- /* Unsupported protocol type, ignore */
- arp_stat.badtype++;
- return;
- }
- if(arp.hwalen > MAXHWALEN || arp.pralen != sizeof(int32)){
- /* Incorrect protocol addr length (different hw addr lengths
- * are OK since AX.25 addresses can be of variable length)
- */
- arp_stat.badlen++;
- return;
- }
- /* If this guy is already in the table, update its entry
- * unless it's a manual entry (noted by the lack of a timer)
- */
- ap = NULLARP; /* ap plays the role of merge_flag in the spec */
- if((ap = arp_lookup(arp.hardware,arp.sprotaddr)) != NULLARP
- && ap->timer.start != 0){
- ap = arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,arp.hwalen & 0xff);
- }
- /* See if we're the address they're looking for */
- if(arp.tprotaddr == ip_addr){
- if(ap == NULLARP) /* Only if not already in the table */
- arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,arp.hwalen & 0xff);
-
- if(arp.opcode == ARP_REQUEST){
- /* Swap sender's and target's (us) hardware and protocol
- * fields, and send the packet back as a reply
- */
- bcopy(arp.shwaddr,arp.thwaddr,arp.hwalen);
- /* Mark the end of the sender's AX.25 address
- * in case he didn't
- */
- if(arp.hardware == ARP_AX25)
- arp.thwaddr[arp.hwalen-1] |= E;
-
- bcopy(interface->hwaddr,arp.shwaddr,at->hwalen);
- arp.tprotaddr = arp.sprotaddr;
- arp.sprotaddr = ip_addr;
- arp.opcode = ARP_REPLY;
- bp = htonarp(&arp);
- (*interface->output)(interface,arp.thwaddr,
- interface->hwaddr,at->arptype,bp);
- arp_stat.inreq++;
- } else {
- arp_stat.replies++;
- }
- }
- }
- /* Add an IP-addr / hardware-addr pair to the ARP table */
- static
- struct arp_tab *
- arp_add(ip_addr,hardware,hw_addr,hw_alen)
- int32 ip_addr; /* IP address, host order */
- int16 hardware; /* Hardware type */
- char *hw_addr; /* Hardware address, if known; NULLCHAR otherwise */
- int16 hw_alen; /* Length of hardware address */
- {
- char *calloc(),*malloc();
- struct arp_tab *arp_lookup();
- void arp_drop();
- struct mbuf *bp,*dequeue();
- register struct arp_tab *ap;
- register struct arp_type *at;
- unsigned hashval,arp_hash();
-
- at = &arp_type[hardware];
- if((ap = arp_lookup(hardware,ip_addr)) == NULLARP){
- /* New entry */
- if((ap = (struct arp_tab *)calloc(1,sizeof(struct arp_tab))) == NULLARP)
- return NULLARP;
- ap->timer.func = arp_drop;
- ap->timer.arg = (int *)ap;
- ap->hardware = hardware;
- ap->ip_addr = ip_addr;
-
- /* Put on head of hash chain */
- hashval = arp_hash(hardware,ip_addr);
- ap->prev = NULLARP;
- ap->next = arp_tab[hashval];
- arp_tab[hashval] = ap;
- if(ap->next != NULLARP){
- ap->next->prev = ap;
- }
- }
- if(hw_addr == NULLCHAR){
- /* Await response */
- ap->state = ARP_PENDING;
- ap->timer.start = PENDTIME;
- } else {
- /* Response has come in, update entry and run through queue */
- ap->state = ARP_VALID;
- ap->timer.start = ARPLIFE;
- if(ap->hw_addr != NULLCHAR)
- free(ap->hw_addr);
- if((ap->hw_addr = malloc(hw_alen)) == NULLCHAR){
- free((char *)ap);
- return NULLARP;
- }
- bcopy(hw_addr,ap->hw_addr,hw_alen);
- /* This kludge marks the end of an AX.25 address to allow
- * for optional digipeaters (insert Joan Rivers salute here)
- */
- if(hardware == ARP_AX25)
- ap->hw_addr[hw_alen-1] |= E;
- while((bp = dequeue(&ap->pending)) != NULLBUF)
- ip_route(bp,0);
- }
- start_timer(&ap->timer);
- return ap;
- }
-
- /* Remove an entry from the ARP table */
- static
- void
- arp_drop(ap)
- register struct arp_tab *ap;
- {
- unsigned arp_hash();
-
- if(ap == NULLARP)
- return;
- stop_timer(&ap->timer); /* Shouldn't be necessary */
- if(ap->next != NULLARP)
- ap->next->prev = ap->prev;
- if(ap->prev != NULLARP)
- ap->prev->next = ap->next;
- else
- arp_tab[arp_hash(ap->hardware,ap->ip_addr)] = ap->next;
- if(ap->hw_addr != NULLCHAR)
- free(ap->hw_addr);
- free_q(&ap->pending);
- free((char *)ap);
- }
-
- /* Look up the given IP address in the ARP table */
- static
- struct arp_tab *
- arp_lookup(hardware,ip_addr)
- int16 hardware;
- int32 ip_addr;
- {
- unsigned arp_hash();
- register struct arp_tab *ap;
-
- for(ap = arp_tab[arp_hash(hardware,ip_addr)]; ap != NULLARP; ap = ap->next){
- if(ap->ip_addr == ip_addr && ap->hardware == hardware)
- break;
- }
- return ap;
- }
- /* Send an ARP request to resolve IP address target_ip */
- static
- void
- arp_output(interface,hardware,target)
- struct interface *interface;
- int16 hardware;
- int32 target;
- {
- struct arp arp;
- struct mbuf *bp,*htonarp();
- struct arp_type *at;
-
- at = &arp_type[hardware];
- if(interface->output == NULLFP)
- return;
-
- arp.hardware = hardware;
- arp.protocol = at->iptype;
- arp.hwalen = at->hwalen;
- arp.pralen = sizeof(int32);
- arp.opcode = ARP_REQUEST;
- bcopy(interface->hwaddr,arp.shwaddr,at->hwalen);
- arp.sprotaddr = ip_addr;
- bzero(arp.thwaddr,at->hwalen);
- arp.tprotaddr = target;
- bp = htonarp(&arp);
- (*interface->output)(interface,at->bdcst,
- interface->hwaddr,at->arptype,bp);
- arp_stat.outreq++;
- }
-
- /* Hash a {hardware type, IP address} pair */
- static
- unsigned
- arp_hash(hardware,ip_addr)
- int16 hardware;
- int32 ip_addr;
- {
- register unsigned hashval;
-
- hashval = hardware;
- hashval ^= hiword(ip_addr);
- hashval ^= loword(ip_addr);
- hashval %= ARPSIZE;
- return hashval;
- }
- /* Copy a host format arp structure into mbuf for transmission */
- #ifdef AMIGA
- /*
- * We play some dirty tricks here. Since the AMIGA is a 68000 based
- * machine, it doesn't take kindly to doing word and long word stores
- * on odd address boundaries. We'll use bcopy() instead. We can do
- * this simply because the 68000 is a big-endian machine, and we don't
- * need to convert to network byte order. This is ugly.
- */
- #endif
- static
- struct mbuf *
- htonarp(arp)
- register struct arp *arp;
- {
- struct mbuf *bp;
- register char *buf;
-
- if(arp == (struct arp *)NULL)
- return NULLBUF;
- if((bp = alloc_mbuf(sizeof(struct arp))) == NULLBUF)
- return NULLBUF;
-
- buf = bp->data;
-
- *(int16 *)buf = htons(arp->hardware);
- buf += sizeof(int16);
-
- *(int16 *)buf = htons(arp->protocol);
- buf += sizeof(int16);
-
- *buf++ = arp->hwalen;
-
- *buf++ = arp->pralen;
-
- *(int16 *)buf = htons(arp->opcode);
- buf += sizeof(int16);
-
- bcopy(arp->shwaddr,buf,arp->hwalen);
- buf += arp->hwalen;
-
- #ifndef AMIGA
- *(int32 *)buf = htonl(arp->sprotaddr);
- #else
- /* we've been alright up to now, but arp->hwalen may have been
- odd, so we don't know if buf is word aligned any more! */
- bcopy(&arp->sprotaddr, buf, sizeof(int32));
- #endif
- buf += sizeof(int32);
-
- bcopy(arp->thwaddr,buf,arp->hwalen);
- buf += arp->hwalen;
-
- #ifndef AMIGA
- *(int32 *)buf = htonl(arp->tprotaddr);
- #else
- bcopy(&arp->tprotaddr, buf, sizeof(int32));
- #endif
- buf += sizeof(int32);
-
- bp->cnt = buf - bp->data;
- return bp;
- }
- /* Convert an incoming ARP packet into a host-format structure */
- static
- int
- ntoharp(arp,bp)
- register struct arp *arp;
- struct mbuf *bp;
- {
- if(arp == (struct arp *)NULL || bp == NULLBUF)
- return -1;
-
- pullup(&bp,(char *)&arp->hardware,sizeof(int16));
- arp->hardware = ntohs(arp->hardware);
-
- pullup(&bp,(char *)&arp->protocol,sizeof(int16));
- arp->protocol = ntohs(arp->protocol);
-
- pullup(&bp,(char *)&arp->hwalen,sizeof(char));
-
- pullup(&bp,(char *)&arp->pralen,sizeof(char));
-
- pullup(&bp,(char *)&arp->opcode,sizeof(int16));
- arp->opcode = ntohs(arp->opcode);
-
- pullup(&bp,arp->shwaddr,arp->hwalen);
-
- pullup(&bp,(char *)&arp->sprotaddr,sizeof(int32));
- arp->sprotaddr = ntohl(arp->sprotaddr);
-
- pullup(&bp,arp->thwaddr,arp->hwalen);
-
- pullup(&bp,(char *)&arp->tprotaddr,sizeof(int32));
- arp->tprotaddr = ntohl(arp->tprotaddr);
-
- free_p(bp);
- return 0;
- }
- #ifdef TRACE
- char *arptypes[] = {
- NULLCHAR,
- "Ethernet",
- "Exp Ethernet",
- "AX.25",
- "Pronet",
- "Chaos"
- };
- int doarpadd(),doarpdrop();
- struct cmds arpcmds[] = {
- "add", doarpadd, 4,
- "usage: arp add <ip addr> ether|ax25 <callsign|ether addr>",
- "arp add failed",
-
- "drop", doarpdrop, 3,
- "usage: arp drop <ip addr> ether|ax25",
- "not in table",
-
- NULLCHAR, NULLFP, 0,
- "arp subcommands: add, drop",
- NULLCHAR,
- };
- int
- doarp(argc,argv)
- int argc;
- char *argv[];
- {
- if(argc < 2){
- dumparp();
- return 0;
- }
- return subcmd(arpcmds,argc,argv);
- }
- static
- doarpadd(argc,argv)
- int argc;
- char *argv[];
- {
- int hardware,hwalen,i;
- int32 addr,aton();
- char *malloc(),*hwaddr;
- int naddr;
- struct arp_tab *ap;
- struct arp_type *at;
- struct ax25_addr *axp;
-
- addr = aton(argv[1]);
- /* This is a kludge. It really ought to be table driven */
- switch(tolower(argv[2][0])){
- case 'e': /* "ether" */
- hardware = ARP_ETHER;
- naddr = 1;
- break;
- case 'a': /* "ax25" */
- hardware = ARP_AX25;
- naddr = argc - 3;
- break;
- default:
- printf("unknown hardware type \"%s\"\r\n",argv[2]);
- return -1;
- }
- /* If an entry already exists, clear it */
- if((ap = arp_lookup(hardware,addr)) != NULLARP)
- arp_drop(ap);
-
- at = &arp_type[hardware];
-
- /* Allocate buffer for hardware address and fill with remaining args */
- hwalen = at->hwalen * naddr;
- if((hwaddr = malloc(hwalen)) == NULLCHAR){
- printf("No space\r\n");
- return 0;
- }
- (*at->scan)(hwaddr,argv[3]); /* Destination address */
-
- /* Special hackery to handle a series of AX.25 digipeaters */
- if(hardware == ARP_AX25){
- axp = (struct ax25_addr *)hwaddr;
- for(i=1;i<naddr;i++){
- /* Set E bit only on last AX.25 call */
- axp->ssid &= ~E;
- /* axp++; */
- axp = (struct ax25_addr *) ((char *)axp + AXALEN);
- (*at->scan)((char *)axp,argv[3+i]);
- }
- }
- ap = arp_add(addr,hardware,hwaddr,hwalen); /* Put in table */
- free(hwaddr); /* Clean up */
- stop_timer(&ap->timer); /* Make entry permanent */
- ap->timer.count = ap->timer.start = 0;
- }
- /* Remove an ARP entry */
- static
- doarpdrop(argc,argv)
- int argc;
- char *argv[];
- {
- int hardware;
- int32 addr,aton();
- struct arp_tab *ap;
-
- addr = aton(argv[1]);
- /* This is a kludge. It really ought to be table driven */
- switch(tolower(argv[2][0])){
- case 'e': /* "ether" */
- hardware = ARP_ETHER;
- break;
- case 'a': /* "ax25" */
- hardware = ARP_AX25;
- break;
- default:
- hardware = 0;
- break;
- }
- if((ap = arp_lookup(hardware,addr)) == NULLARP)
- return -1;
- arp_drop(ap);
- return 0;
- }
- /* Dump ARP table */
- static
- dumparp()
- {
- register int i;
- extern struct arp_stat arp_stat;
- register struct arp_tab *ap;
- char e[128];
- char *inet_ntoa();
-
- printf("received %u badtype %u reqst in %u replies %u reqst out %u\r\n",
- arp_stat.recv,arp_stat.badtype,arp_stat.inreq,
- arp_stat.replies,arp_stat.outreq);
-
- printf("IP addr Type Time Q Addr\r\n");
- for(i=0;i<ARPSIZE;i++){
- for(ap = arp_tab[i];ap != (struct arp_tab *)NULL;ap = ap->next){
- printf("%-16s",inet_ntoa(ap->ip_addr));
- printf("%-9s",arptypes[ap->hardware]);
- printf("%-5ld",ap->timer.count*(long)MSPTICK/1000);
- if(ap->state == ARP_PENDING)
- printf("%-2u",len_q(ap->pending));
- else
- printf(" ");
- if(ap->state == ARP_VALID){
- if(arp_type[ap->hardware].format != NULLFP){
- (*arp_type[ap->hardware].format)(e,ap->hw_addr);
- } else {
- e[0] = '\0';
- }
- printf("%s",e);
- } else {
- printf("[unknown]");
- }
- printf("\r\n");
- }
- }
- return 0;
- }
- /* Dump ARP packets (incomplete) */
- static char *hwtypes[] = {
- "",
- "10 Mb Ethernet",
- "3 Mb Ethernet",
- "AX.25",
- NULLCHAR,
- };
- #define NHWTYPES 4
- arp_dump(bp)
- struct mbuf *bp;
- {
- struct arp arp;
- struct mbuf *tbp;
- char *inet_ntoa();
-
- if(bp == NULLBUF)
- return;
- /* Make temporary copy */
- dup_p(&tbp,bp,0,len_mbuf(bp));
- ntoharp(&arp,tbp);
-
- if(arp.hardware < NHWTYPES)
- printf("ARP: hwtype %s",hwtypes[arp.hardware]);
- else
- printf("ARP: hwtype %u",arp.hardware);
- printf(" prot 0x%x hwlen %u prlen %u",
- arp.protocol,arp.hwalen,arp.pralen);
- switch(arp.opcode){
- case ARP_REQUEST:
- printf(" op REQUEST");
- break;
- case ARP_REPLY:
- printf(" op REPLY");
- break;
- default:
- printf(" op %u",arp.opcode);
- break;
- }
- printf(" target %s\r\n",inet_ntoa(arp.tprotaddr));
- }
- #endif
-